package sanshui.system.trade.factory.trade.worker;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;
import sanshui.system.trade.common.CommonResponse;
import sanshui.system.trade.common.GlobalContain;
import sanshui.system.trade.common.TradeType;
import sanshui.system.trade.config.SpringContextHolder;
import sanshui.system.trade.db.entity.TradeHisOpPos;
import sanshui.system.trade.db.entity.TradeLimitOrder;
import sanshui.system.trade.db.entity.TradeReport;
import sanshui.system.trade.db.entity.TradeWorker;
import sanshui.system.trade.db.mapper.StockATradeCalMapper;
import sanshui.system.trade.factory.data.*;
import sanshui.system.trade.factory.distribute.DistributeManager;
import sanshui.system.trade.factory.notify.LogNotifyFactory;
import sanshui.system.trade.factory.notify.TradeSysNotify;
import sanshui.system.trade.factory.notify.TradeSysNotifyModel;
import sanshui.system.trade.factory.notify.TradeSysNotifyService;
import sanshui.system.trade.factory.report.ReportTask;
import sanshui.system.trade.factory.timeManager.TimeManager;
import sanshui.system.trade.factory.timeManager.type.BackTradeTimeManager;
import sanshui.system.trade.factory.timeManager.type.RtTimeManager;
import sanshui.system.trade.factory.trade.TradeSystemContext;
import sanshui.system.trade.factory.trade.indicator.*;
import sanshui.system.trade.factory.trade.riskCtr.AbstractRiskStrategy;
import sanshui.system.trade.factory.trade.strategy.*;
import sanshui.system.trade.model.*;
import sanshui.system.trade.service.*;
import sanshui.system.trade.util.CommonTimeUtil;
import sanshui.system.trade.util.IdWorkerUtil;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;

@Slf4j
public abstract class AbstractTradeWorker implements Worker, Callable<WorkerResult> {
    private boolean stop = false;

    protected String workerName;
    protected Long workerId;
    protected Long accId;
    List<String> targetCodes; // 自定义标的集合
    List<String> targetCodesIndex; // 自定义标的集合
    TradeSysWorkerModel tradeModel;
    DistributeManager distributeManager;

    // 多线程指标执行器
    StrategyExecutor strategyExecutor;
    /**
     * {"000001.SZ":["M5","EM5"]}
     */
    Map<String, List<IndicatorResult>> indicatorResultMap;
    Map<String, TradeStrategy> targetStrategyMap;
    IAccountService accountService = SpringContextHolder.getBean(IAccountService.class);
    IWorkerService workerService = SpringContextHolder.getBean(IWorkerService.class);
    IBuySellService buySellService = SpringContextHolder.getBean(IBuySellService.class);
    ITradeHisOpPosService hisOpPosService = SpringContextHolder.getBean(ITradeHisOpPosService.class);
    ITradeLimitOrderService limitOrderService = SpringContextHolder.getBean(ITradeLimitOrderService.class);
    StrategyFactory strategyFactory = SpringContextHolder.getBean(StrategyFactory.class);
    DataFactory dataFactory = SpringContextHolder.getBean(DataFactory.class);
    TradeSysNotifyService notifyService = SpringContextHolder.getBean(TradeSysNotifyService.class);
    ReportTask reportTask = SpringContextHolder.getBean(ReportTask.class);
    IdWorkerUtil idWorkerUtil = SpringContextHolder.getBean(IdWorkerUtil.class);

    protected TradeSystemContext tradeSystemContext; // 所有缓存都在上下文
    // 交易系统类型：实时、回测
    TradeSystemType tradeSystemType;
    // 交易市场：A股、OKEX
    TradeMarketKey tradeMarketKey;
    // 交易市场的数据类型: T0\T1
    DataMarketType dataMarketType;
    // 时间驱动管理器
    TimeManager timeManager;
    // 数据缓存器
    DataCache dataCache;
    // 最低轮训时间,非实时系统为0
    Long minSleepTime = 10L;

    public AbstractTradeWorker(TradeSysWorkerModel tradeModel, DataCache dataCache) {
        this.tradeSystemContext = new TradeSystemContext();
        this.tradeSystemContext.setUserId(tradeModel.getTradeAccountModel().getUserId());
        this.tradeSystemContext.setAccId(tradeModel.getTradeAccountModel().getAccId());
        this.tradeSystemContext.setWorkerId(tradeModel.getWorkerId());
        this.tradeSystemContext.setDataCache(dataCache);
        this.tradeSystemContext.setAccount(tradeModel.getTradeAccountModel());
        this.tradeSystemContext.setDistributeManager(this.distributeManager);
        this.tradeSystemContext.setPositionModelList(new ArrayList<>()); // 从数据库读取
        this.tradeSystemContext.setTradeMarketKey(tradeModel.getTradeMarketKey());
        this.tradeSystemContext.setTradeSystemType(tradeModel.getTradeSystemType());
        this.tradeSystemContext.setDataMarketType(tradeModel.getDataMarketType());

        this.tradeSystemType = tradeModel.getTradeSystemType();
        this.dataMarketType = tradeModel.getDataMarketType();
        this.tradeMarketKey = tradeModel.getTradeMarketKey();
        this.dataCache = dataCache;

        if (null == tradeModel.getTargetCodesIndex()) {
            this.targetCodesIndex = new ArrayList<>();
        } else {
            this.targetCodesIndex = tradeModel.getTargetCodesIndex();
        }

        if (TradeSystemType.RT.compareTo(tradeSystemType) == 0) {
            this.timeManager = new RtTimeManager(tradeModel.getFrequency(), dataCache.getStartTime(), dataCache.getEndTime());
        } else {
            if (this.dataCache.getEndTime() == null) {
                this.dataCache.setEndTime(CommonTimeUtil.getDate1500(LocalDateTime.now()));
            }
            this.minSleepTime = 0L;
            this.timeManager = new BackTradeTimeManager(tradeModel.getFrequency(), dataCache.getStartTime(), dataCache.getEndTime());
        }

        this.tradeSystemContext.setTimeManager(this.timeManager);

        this.workerName = tradeModel.getWorkerName();
        this.tradeModel = tradeModel;

        this.indicatorResultMap = new HashMap<>();
        this.targetStrategyMap = new HashMap<>();
        if (null == this.tradeSystemContext.getDataCache() ||
                null == this.tradeSystemContext.getAccount()) {
            log.error("初始化失败");
            throw new IllegalArgumentException("初始化参数不足");
        }

        if (null == this.tradeModel.getFilterModel() && TradeMarketKey.A_GU.compareTo(this.tradeModel.getTradeMarketKey()) == 0) {
            this.tradeModel.setFilterModel(FilterModel.builder()
                    .isZB(true).isCY(true)
                    .isST(false).isXSB(false).isKC(false).isBJ(false)
                    .build());
        }

        if(DataMarketType.T1.name().equalsIgnoreCase(tradeModel.getFrequency())){
            // 如果是T1的交易，需要等到数据准备好以后再跑
            String exeTime4ForT1 = this.tradeModel.getExeTime4ForT1();
            if (Strings.isEmpty(exeTime4ForT1)){
                this.tradeModel.setExeTime4ForT1("144000"); // 固定的执行时间
            }
        }

        this.tradeModel.setMillis(this.minSleepTime);
        this.workerId = tradeModel.getWorkerId();
        this.accId = tradeModel.getTradeAccountModel().getAccId();
    }

    protected abstract void updateAccountToCacheInRt();
    protected abstract boolean needWaitData(LocalDateTime currentTime) throws Exception;

    /**
     * 不是交易时间可以直接算运行了
     * */
    protected boolean isWeek(LocalDateTime currentTime) throws Exception{
        return false;
    };


    protected abstract Bar parseOri2Tick(StockInfoModel stockInfoVo);

    /**
     * 使用交易缓存来判断
     * 买入： 必须满足可以按照约定买入
     * 卖出：
     */
    protected abstract boolean isValidInThisAccountUseCache(List<StrategyResult> endStrategyCalls) throws Exception;

    protected void notifyInfo(TradeSystemContext context, GlobalContain.NotifyType notifyType) {
    }

    protected void notifyBSInfo(TradeSystemContext context, List<StrategyResult> strategyResults) {
        TradeSysNotifyModel notifyModel = new TradeSysNotifyModel();
        notifyModel.setNotifyType(GlobalContain.NotifyType.BS_SINGLE);
        if (TradeSystemType.BACK_TRADE.compareTo(context.getTradeSystemType()) == 0){
            log.info("回测中，不需要通知");
            return;
        }
        notifyModel.setNotify(false);

        Long userId = context.getUserId();
        notifyModel.setUserId(userId);

        //TODO: 查库，该用户是否发起通知，哪些通道进行通知
        notifyModel.setTitle("[信号][买卖]");
        notifyModel.setOriContent(LogNotifyFactory.getNotifyContentInfoByBsWithTxt(strategyResults));
        notifyModel.setToSomeJsonStr(""); // 接收者的信息
        // 拿到具体实现类
        TradeSysNotify notify = notifyService.getNotifyService(GlobalContain.NotifyType.BS_SINGLE.name());
        // 发送
        notify.execute(notifyModel);
    }

    protected abstract void updateHoldPos(TradeSystemContext tradeSystemContext);
    /**
     * 交易系统初始化
     */
    public boolean initTradeSystem() throws Exception {
        log.info("【开始】初始化交易系统, accountId:{}", this.tradeSystemContext.getAccount().getAccId());

        if (this.tradeSystemContext.getStrategy() == null){
            TradeStrategy strategy = strategyFactory.getTradeStrategy(tradeModel.getTradeStrategyCode());
            log.info("初始化交易策略[{}]...", strategy.getStrategyCode().getMessage());
            this.tradeSystemContext.setStrategy(strategy);
        }

        if (this.tradeSystemContext.getRiskStrategy() == null) {
            AbstractRiskStrategy riskStrategy = strategyFactory.getRiskStrategy(tradeModel.getRiskCtrCode());
            log.info("初始化风控策略[{}]...", riskStrategy.getRiskName().getMessage());
            this.tradeSystemContext.setRiskStrategy(riskStrategy);
        }

        targetCodes = this.dataCache.getTargetCodes();
        if (!this.targetCodesIndex.isEmpty()) {
            log.info("加载指数下的所有股票");
            for (String codesIndex : this.targetCodesIndex) {
                log.info("加载指数：{}下的所有股票", codesIndex);
                List<String> indexTargets = dataFactory.loadIndexTargetCode(codesIndex);// TODO
                targetCodes.addAll(indexTargets);
            }
        }

        // 清洗code，都使用大写
        List<String> clear = new ArrayList<>();
        for (String targetCode : targetCodes) {
            clear.add(targetCode.toUpperCase());
        }
        targetCodes = clear;
        this.dataCache.setTargetCodes(targetCodes);

        if (TradeMarketKey.A_GU.compareTo(tradeMarketKey) == 0) {
            // 加载历史数据, 并为时间驱动器赋值
            log.info("加载目标股票历史数据...");
            this.dataCache.loadHisData(timeManager, this::parseOri2Tick);
            targetCodes = this.dataCache.getTargetCodes();
        }

        /**
         *  指标集合：将一组股票代码封装为指标，不同的指标加入到strategyExecutorList
         *  将每一个指标做成一个多线程执行器，默认单线程
         */
        if (this.buildIndicatorServiceList()) {
            log.info("多线程指标池完成初始化");
        }
        if (targetCodes == null || CollectionUtils.isEmpty(targetCodes)) {
            throw new RuntimeException("没有目标代码");
        }

        // 异步数据库交互
        this.distributeManager = new DistributeManager();

        // 初始化上下文中的账户信息和持仓信息
        if (TradeSystemType.RT.compareTo(this.tradeSystemType) == 0) {
            log.info("初始化持仓信息...");
            Map<String, PositionAccModel> hisHoldPos = hisOpPosService.selectAccHoldPosModels(accId, CommonTimeUtil.getNowTime16());
            this.tradeSystemContext.setHoldPositionMap(hisHoldPos);
        }

        log.info("【结束】初始化交易系统, accountId:{}", this.tradeSystemContext.getAccount().getAccId());
        return true;
    }

    @Override
    public WorkerResult run() {
        try {
            DataCache dataCache = tradeSystemContext.getDataCache();

            if (CollectionUtils.isEmpty(this.targetCodes)) {
                this.targetCodes = dataCache.getTargetCodes();
            }

            // 代码过滤
            if (null != tradeModel.getFilterModel()) {
                this.filterTargetCode();
            }

            accountService.updateAccStatus(accId, TradeType.AccountStatus.IN.getIndex(), 0);
            // timeManager.hasNext()在实时系统中，如果没有时间，会根据上次时间自动生成
            // 关键代码：CommonTimeUtil.timestampAdd(timestampCurrent, frequency);
            while (timeManager.hasNext() && this.isRunning()) {
                // 根据当前时间更新数据
                LocalDateTime currentTime = timeManager.next();
                if (null != currentTime) {
                    // 保证timestampCurrent不会为null，始终记录上次时间，给实时系统制造驱动时间
                    timeManager.timestampCurrent = currentTime;
                }
                log.info("[当前时间][{}][时间周期][{}][最近驱动时间][{}][正在驱动时间][{}][账号][{}][WORKER][{}]---", CommonTimeUtil.getNowTime16(), timeManager.frequency, timeManager.timestampCurrent ,currentTime, accId, workerId);
                // 此时currentTime可能为null，用于清除历史数据，进入实时交易阶段
                if (!this.continueTradeClear(currentTime, dataCache)) {
                    return WorkerResult.success("时间用完，结束交易", null);
                }

                // 更新最新数据到 currentMap
                boolean upFlg = dataCache.update(currentTime, tradeSystemType);
                if (!upFlg){ // 只有回测系统可能更新失败
                    log.error("时间周期结束, 或者交易系统类型不存在，终止运行，tradeSystemType:{}", tradeSystemType);
                    break;
                }

                if (this.isWeek(currentTime)){
                    // 更新交易时间
                    log.info("周末时间:{}", currentTime);
                    // 检查系统分发器是否繁忙, 繁忙则增加每次的等待时间
                    this.busyCheck(true);
                    this.tradeSystemContext.updateTradeDate(currentTime);
                    timeManager.pollNext(); // 将使用过的时间抛出
                    continue;
                }

                this.busyCheck(false);
                // 通常回测系统不用等待，这里是去取实时数据，没有取到才需要等待
                // 实时系统是直接取数据库中最近的交易数据
                // currentTime为null时是与下次驱动到空档期，应该等待到驱动时间出现
                // TODO：实时系统很可能在睡眠时间内无法拿到所有目标的数据
                boolean needWait = this.needWaitData(currentTime);
                if (needWait) {
                    // 只有实时系统才会等待，等待时需要把推出到时间再塞回去？
                    continue;
                }

                Map<String, Bar> next = dataCache.getcurrentBar();
                if (next.isEmpty()) {
                    // 只有回测系统会出现，实时系统会在上面等待
                    timeManager.pollNext();
                    continue;
                }
                // 更新交易时间
                this.tradeSystemContext.updateTradeDate(currentTime);

                // 实时更新
                this.updateContext(true);

                // 选股--支持单指标选股或者多指标选股
                this.computeIndicator(next, this.tradeSystemContext);

                // 实时系统需要关注实际账户信息：可能存在手动修改账户的情况
                this.updateAccountToCacheInRt();

                // 获取多线程指标结果
                Map<String, IndicatorResult> indicatorResults = this.getIndictsResult();

                // 运行交易策略--控制交易信号，上穿买入下穿卖出等
                List<StrategyResult> strategyResults = this.getStrategyResults(indicatorResults);
                tradeSystemContext.setStrategyResults(strategyResults);

                // 异步通知--买卖信号
                this.notifyInfo(tradeSystemContext, GlobalContain.NotifyType.BS_SINGLE);

                // 仓位管理策略--控制交易价格、数量
                strategyResults = this.tradeSystemContext.getRiskStrategy().exec(this.tradeSystemContext, strategyResults);
                tradeSystemContext.setStrategyRiskResults(strategyResults);

                // 计算手续费
                this.computeFee(strategyResults);

                // 处理交易数量，默认A股
                this.dealWithTradeNum(strategyResults);

                // 异步通知--发生交易
                this.notifyInfo(tradeSystemContext, GlobalContain.NotifyType.TRADE);

                // 校验当前账户状态是否可以完成交易
                if (!isValidInThisAccountUseCache(strategyResults)) {
                    log.info("当前账户无法完成本次交易");
                    continue;
                }

                // 根据策略结果进行异步交易操作，并更新缓存
                this.tradeAsyncAndUpdate(strategyResults);

                // 实时更新
                this.updateContext(true);

                // 周期结束
                this.endFrequency();

                this.sleep(this.tradeModel.getMillis());
                timeManager.pollNext(); // 将使用过的时间抛出
                if (null == timeManager.tryNextTime()) {
                    // 尝试获取下一个时间片失败，说明开始实时计算或者回测结束
                    // 清理历史数据释放内存
                    dataCache.clear();
                }
            }
        } catch (Exception e) {
            log.error("交易系统执行出错", e);
            TradeWorker tradeWorker = new TradeWorker();
            tradeWorker.setWorkerStatus(TradeType.WorkerStatus.END.getCode());
            tradeWorker.setWorkerId(workerId);
            workerService.updateWorkerByWorkerId(workerId, tradeWorker);
            return WorkerResult.fail("交易系统执行出错:" + e.getMessage());
        } finally {
            this.sleep(2000L);
            this.reportExportEnd();
        }

        return WorkerResult.success("执行完成", null);
    }


    /**
     * 交易系统 -- 指标计算执行
     * return:
     */
    public void computeIndicator(Map<String, Bar> stockInfoVoMap, TradeSystemContext tradeSystemContext) throws Exception {
        if (this.targetCodes.isEmpty()) {
            return;
        }
        log.info("计算指标中..., workerId:{}", tradeSystemContext.getWorkerId());
        strategyExecutor.submit(stockInfoVoMap, tradeSystemContext);
    }

    private Map<String, IndicatorResult> getIndictsResult() {
        log.info("获取指标结果..., workerId:{}", tradeSystemContext.getWorkerId());
        List<IndicatorResult> results = strategyExecutor.takeResults();
        return buildIndicatorResultMap(results);
    }

    void sleep(long millis) {
        if (millis <= 100) {
            return;
        }
        try {
            log.info("正在睡眠, 时间:{}毫秒", millis);
            Thread.sleep(millis);
        } catch (Exception e) {
            log.error("睡眠失败");
        }
    }

    private void filterTargetCode() {
        if (null != targetCodes) {
//            targetCodes = tradeDataContainer.getAllCodesFilter(filterModel);
        }
    }

    /**
     * 回测系统，如果时间驱动器返回时间为NULL，则停止
     * 实时系统，实时时间驱动器一般不会返回NULL，如果返回NULL，等待即可
     */
    protected boolean continueTradeClear(LocalDateTime currentTime, DataCache dataCache) {
        throw new IllegalArgumentException("必须实现continueTrade方法");
    }


    @Override
    public WorkerResult call() throws Exception {
        WorkerResult taskResult = new WorkerResult();

        try {
            /**
             * 交易初始化：账户、分发器
             * */
            if (!this.initTradeSystem()) {
                log.error("交易系统初始化失败");
                return WorkerResult.fail("交易系统初始化失败");
            }

            log.info("成功创建交易系统，交易市场：{}， 市场数据结算类型：{}，交易系统类型：{}， 交易频率：{}；历史数据，开始时间：{}， 结束时间：{}， 标的数量：{}",
                    tradeMarketKey, dataMarketType, tradeSystemType, this.timeManager.frequency, this.timeManager.startTime, this.timeManager.endTime, this.targetCodes.size());

            taskResult = this.run();
        } catch (Exception e) {
            log.error("交易系统发生错误, tradeName:{}", workerName, e);
            return WorkerResult.fail("交易系统发生未知错误");
        } finally {
            log.info("主动关闭并清理内存");
            this.stop();
        }
        return taskResult;
    }

    @Override
    public boolean isRunning() {
        TradeWorker tradeWorker = workerService.selectWorkerById(workerId);
        return !stop && !TradeType.WorkerStatus.END.getCode().equals(tradeWorker.getWorkerStatus());
    }

    @Override
    public void stop() {
        this.stop = true;
        workerService.updateWorkerStatusByWorkerId(workerId, TradeType.WorkerStatus.END, TradeType.WorkerStatus.IN);
        accountService.updateAccStatus(accId, TradeType.AccountStatus.END.getIndex(), TradeType.WorkerStatus.IN.getIndex());
        if (null != this.strategyExecutor) this.strategyExecutor.close();
        if (null != this.distributeManager) {
            int index = 0;
            while (!this.distributeManager.isEmpty() && index < 30) {
                try {
                    Thread.sleep(1000L);
                    index++;
                    log.info("等待分发器释放：{}秒，30秒超时", index);
                } catch (Exception e) {
                }
            }
            this.distributeManager.close();

        }
        if (null != this.tradeSystemContext) this.tradeSystemContext.clear();
        if (null != this.targetStrategyMap) this.targetStrategyMap = null;
        if (null != this.indicatorResultMap) this.indicatorResultMap = null;
        log.info("所有内存释放完成");
    }

    /**
     * 生成最终报告
     */
    protected void reportExportEnd() {
        log.info("生成最终报告, accId:{}", tradeSystemContext.getAccount().getAccId());
        TradeReportRequestModel requestModel = new TradeReportRequestModel();
        requestModel.setStartDate(CommonTimeUtil.getNowTime8(this.timeManager.startTime));
        requestModel.setEndDate(CommonTimeUtil.getNowTime8(this.timeManager.endTime));
        requestModel.setAccId(tradeSystemContext.getAccount().getAccId().toString());
        requestModel.setWorkerId(tradeSystemContext.getWorkerId().toString());
        requestModel.setInitValue(String.valueOf(tradeSystemContext.getAccount().getInitValue()/100));
        requestModel.setPicPath("./log");
        TradeReport tradeReport = reportTask.generateBackTradeReportByPython(requestModel);
        log.info("最终报告, report:{}", JSONObject.toJSONString(tradeReport));
        if (null != tradeReport){
            TradeReportModel reportModel = new TradeReportModel();
            tradeReport.setAccId(tradeSystemContext.getAccount().getAccId());
            tradeReport.setWorkerId(tradeSystemContext.getWorkerId());
            BeanUtils.copyProperties(tradeReport, reportModel);
            this.distributeManager.addReport(reportModel);

            TradeWorker tradeWorker = new TradeWorker();
            tradeWorker.setReportId(tradeReport.getReportId());
            workerService.updateWorkerByWorkerId(workerId, tradeWorker);
        }
    }

    /**
     * 创建多线程指标执行器
     * <p>
     * 执行器中线程数量：strategyExecutor
     */
    protected boolean buildIndicatorServiceList() throws Exception {
        TradeStrategy strategy = this.tradeSystemContext.getStrategy();
        for (String targetCode : targetCodes) {
            TradeStrategy tradeStrategy = strategyFactory.getTradeStrategy(strategy.getStrategyCode());
            targetStrategyMap.put(targetCode, tradeStrategy);
        }

        strategyExecutor = new StrategyExecutor(2, targetStrategyMap);
        return true;
    }

    private List<String> parseChooseResult(List<IndicatorResult> indicatorResultList, StrategyCallType callType) {
        List<String> result = new ArrayList<>();
        if (StrategyCallType.BUY.compareTo(callType) == 0) {
            for (IndicatorResult indicatorResult : indicatorResultList) {
                if (StrategyCallType.BUY.compareTo(indicatorResult.getStrategyCallType()) == 0) {
                    result.add(indicatorResult.getCode());
                }
            }
        }

        if (StrategyCallType.SELL.compareTo(callType) == 0) {
            for (IndicatorResult indicatorResult : indicatorResultList) {
                if (StrategyCallType.SELL.compareTo(indicatorResult.getStrategyCallType()) == 0) {
                    result.add(indicatorResult.getCode());
                }
            }
        }
        return result;
    }

    /**
     * 仓位
     */
    protected boolean tradeAsyncAndUpdate(List<StrategyResult> strategyResults) {
        boolean cg = false;
        boolean sendTradeFlg = false;

        String nowTime16 = CommonTimeUtil.getNowTime16();
        LocalDateTime currentTime = timeManager.next();
        LocalDateTime nowTime = CommonTimeUtil.getTimestamp(nowTime16);
        if (!CommonTimeUtil.compare(currentTime, nowTime, 1L, ChronoUnit.DAYS)){
            // 是否相差1天及以上
            log.info("允许发起真实交易, 正在驱动时间:{}, 当前系统时间:{}", currentTime, nowTime16);
            sendTradeFlg = true;
        } else {
            log.warn("仅记录交易, 正在驱动时间:{}, 当前系统时间:{}", currentTime, nowTime16);
        }
        CommonResponse<Boolean> operation = new CommonResponse<>();
        operation.setData(true);

        for (StrategyResult endStrategyCall : strategyResults) {
            endStrategyCall.setTradeTime(this.tradeSystemContext.getTradeTime());
            StrategyCallType strategyCallType = endStrategyCall.getStrategyCallType();
            if (StrategyCallType.BUY.compareTo(strategyCallType) == 0 &&
                    endStrategyCall.getNum() > 0) {
                log.info("买入, time:{}, codeName:{}, price:{}, num:{}", tradeSystemContext.getTradeTime(), endStrategyCall.getName(), endStrategyCall.getPrice(), endStrategyCall.getNum());
                if (sendTradeFlg){
                    operation = buySellService.operationAsync(
                            this.tradeSystemContext.getAccount().getAccId().toString(),
                            new BigDecimal(endStrategyCall.getPrice()),
                            new BigDecimal(endStrategyCall.getNum()),
                            endStrategyCall.getCode(),
                            endStrategyCall);
                }
                if (operation.getData()) {
                    log.info("买入--成功");
                    endStrategyCall.setTradeTime(this.tradeSystemContext.getTradeTime());
                    this.insertPositionCacheAndDb(endStrategyCall); // 更新缓存
                    this.updateAccountCacheAndDb(endStrategyCall);
                    cg = true;
                }
            }

            if (StrategyCallType.SELL.compareTo(strategyCallType) == 0 &&
                    endStrategyCall.getNum() > 0) {
                log.info("卖出, time:{}, codeName:{}, price:{}, num:{}", endStrategyCall.getTradeTime(), endStrategyCall.getName(), endStrategyCall.getPrice(), endStrategyCall.getNum());
                if (sendTradeFlg) {
                    operation = buySellService.operationAsync(
                            this.tradeSystemContext.getAccount().getAccId().toString(),
                            new BigDecimal(endStrategyCall.getPrice()),
                            new BigDecimal(endStrategyCall.getNum()),
                            endStrategyCall.getCode(),
                            endStrategyCall);
                }
                if (operation.getData()) {
                    log.info("卖出--成功");
                    this.insertPositionCacheAndDb(endStrategyCall); // 更新缓存，并且异步向数据库插入持仓
                    this.updateAccountCacheAndDb(endStrategyCall);
                    cg = true;
                }
            }
        }
        return cg;
    }

    /**
     * 更新缓存并向分发器发起数据库操作
     */
    private void insertPositionCacheAndDb(StrategyResult endStrategyCall) {
        TradeAccountModel account = tradeSystemContext.getAccount();
        DataCache dataContainer = tradeSystemContext.getDataCache();

        TradeHisOpPos tradeHisPosition = accountService.buildOpPos(
                account.getAccId(),
                this.tradeModel.getWorkerId(),
                endStrategyCall.getPrice(), endStrategyCall.getNum(),
                endStrategyCall.getFee(), endStrategyCall.getCode(), endStrategyCall.getStrategyCallType(),
                tradeSystemContext.getTradeTime());

        TradeHisPositionModel tradeHisPositionModel = new TradeHisPositionModel();
        tradeHisPositionModel.setDataMarketType(dataContainer.getDataMarketType());
        BeanUtil.copyProperties(tradeHisPosition, tradeHisPositionModel);
        tradeSystemContext.addPosInHisList(tradeHisPositionModel);
        distributeManager.addPosition(tradeHisPositionModel);
    }

    /**
     * 更新账户缓存以及异步更新数据库
     * 买入：插入一条新的数据
     * 卖出：插入一条新的数据
     */
    private void updateAccountCacheAndDb(StrategyResult endStrategyCall) {
        TradeAccountModel account = this.tradeSystemContext.getAccount();
        Long accId = account.getAccId();
        BigDecimal price = new BigDecimal(endStrategyCall.getPrice());
        BigDecimal num = new BigDecimal(endStrategyCall.getNum());
        StrategyCallType operationType = endStrategyCall.getStrategyCallType();
        BigDecimal fee = new BigDecimal(endStrategyCall.getFee());

        BigDecimal willHoldValue = price.multiply(num);

        BigDecimal cashValue = new BigDecimal(account.getCurrentCash());
        if (StrategyCallType.BUY.compareTo(operationType) == 0) {
            cashValue = cashValue.subtract(willHoldValue).subtract(fee);
        }

        if (StrategyCallType.SELL.compareTo(operationType) == 0) {
            cashValue = cashValue.add(willHoldValue).subtract(fee);
        }

        account.setCurrentCash(cashValue.longValue());
        distributeManager.addAccount(account);
    }

    public String getWorkerName() {
        return workerName;
    }

    public Long getAccId() {
        return accId;
    }

    public Long getWorkerId() {
        return workerId;
    }

    /**
     * indicatorCodeResultMap: 指标code->一组股票
     * return: 股票标的->一组指标
     */
    private Map<String, IndicatorResult> buildIndicatorResultMap(List<IndicatorResult> indicatorResults) {
        Map<String, IndicatorResult> codeIndicatorMap = new HashMap<>();
        for (IndicatorResult indicatorResult : indicatorResults) {
            codeIndicatorMap.put(indicatorResult.getCode(), indicatorResult);
        }

        return codeIndicatorMap;
    }

    /**
     * 针对缓存
     * A股计算仓位信息，可交易数量，成本价格等
     * 默认：数量、价格（分）都为整数Long
     */
    protected List<PositionAccModel> accPositionsStatistic(List<TradeHisPositionModel> positions, LocalDateTime currentTime) throws Exception {
        Map<String, List<TradeHisPositionModel>> codePosition = new HashMap<>();
        List<PositionAccModel> positionAccModels = new ArrayList<>();
        for (TradeHisPositionModel position : positions) {
            List<TradeHisPositionModel> orDefault = codePosition.getOrDefault(position.getTargetCode(), null);
            if (null == orDefault) {
                codePosition.put(position.getTargetCode(), new ArrayList<>());
            } else {
                orDefault.add(position);
                codePosition.put(position.getTargetCode(), orDefault);
            }
        }

        for (List<TradeHisPositionModel> positionModels : codePosition.values()) {
            PositionAccModel positionAccModel = this.accPositionsOneCodeStatistic(positionModels, currentTime);
            if (positionAccModel != null) {
                positionAccModels.add(positionAccModel);
            }
        }

        return positionAccModels;
    }

    /**
     * 计算当前建仓区间内可交易数量
     * 并帮助更新数据库
     */
    protected PositionAccModel accPositionsOneCodeStatistic(List<TradeHisPositionModel> positions, LocalDateTime currentTime) throws Exception {
        long trueBuyNum = 0L;
        long trueSellNum = 0L;
        long trueSellValue = 0L;
        long trueBuyValue = 0L;
        long canTradeNum = 0L; // 总的可交易金额

        String code = "";

        for (TradeHisPositionModel position : positions) {
            Long price = position.getTradePrice();
            Long fee = position.getFee();
            String targetCode = position.getTargetCode();
            Long stockNum = position.getTradeNum();
            String bsTime = position.getOpTime();
            String opType = position.getOpType();

            if (Strings.isEmpty(code)) {
                code = targetCode;
            } else if (!code.equalsIgnoreCase(targetCode)) {
                // 入参集合中code不唯一
                throw new IllegalArgumentException("入参positions中code不唯一");
            }

            if (StrategyCallType.SELL.name().equals(opType)) {
                trueSellNum = trueSellNum + stockNum;
                trueSellValue = trueSellValue + stockNum * price - fee; // 卖出获取的现金
            }

            if (StrategyCallType.BUY.name().equals(opType)) {
                trueBuyNum = trueBuyNum + stockNum;
                trueBuyValue = trueBuyValue + stockNum * price + fee; // 买入消耗现金
                if (DataMarketType.T1.compareTo(position.getDataMarketType()) == 0 &&
                        CommonTimeUtil.compare(CommonTimeUtil.getTimestamp(bsTime), currentTime, 1, ChronoUnit.DAYS)) {
                    canTradeNum += stockNum;
                } else if (DataMarketType.T0.compareTo(position.getDataMarketType()) == 0) {
                    canTradeNum += stockNum;
                }
            }
        }

        // 本次建仓交易金额差, 成本
        Long deltaValue = trueBuyValue - trueSellValue;
        // 总共剩余的数量
        Long deltaNum = trueBuyNum - trueSellNum;
        canTradeNum -= trueSellNum;
        if (deltaNum <= 0) {
            return null;
        } else {
            PositionAccModel positionAccModel = new PositionAccModel();
            positionAccModel.setTsCode(code);
            positionAccModel.setNumTotal(deltaNum);
            positionAccModel.setNumCanTrade(canTradeNum);
            positionAccModel.setAveragePrice(deltaValue / deltaNum);
            return positionAccModel;
        }
    }

    private NotifyModel buildNotifyModel(List<StrategyResult> strategyResults, String title, String content, GlobalContain.NotifyType notifyType) {
        StringBuilder contents = new StringBuilder(title + "\n");
        contents.append(content);
        if (GlobalContain.NotifyType.SYSTEM.compareTo(notifyType) == 0) {
            contents.append("[系统]");
        } else if (GlobalContain.NotifyType.BS_SINGLE.compareTo(notifyType) == 0) {
            for (StrategyResult strategyResult : strategyResults) {
                String code = strategyResult.getCode();
                Float num = strategyResult.getNum()/100.0f;
                Float price = strategyResult.getPrice()/100.0f;
                if (StrategyCallType.BUY.compareTo(strategyResult.getStrategyCallType()) == 0) {
                    log.info(String.format("[信号][买入]目标:%s, 价格:%s元 \n", code, price));
                    contents.append(String.format("[信号][买入]目标:%s, 价格:%s元 \n", code, price));
                }
                if (StrategyCallType.SELL.compareTo(strategyResult.getStrategyCallType()) == 0) {
                    log.info(String.format("[信号][卖出]目标:%s, 价格:%s元 \n", code, price));
                    contents.append(String.format("[信号][卖出]目标:%s, 价格:%s元 \n", code, price));
                }
            }
        } else if (GlobalContain.NotifyType.TRADE.compareTo(notifyType) == 0) {
            for (StrategyResult strategyResult : strategyResults) {
                if (StrategyCallType.BUY.compareTo(strategyResult.getStrategyCallType()) == 0) {
                    contents.append(String.format("[操作][买入]目标:%s, 数量:%s, 价格:%s \n", strategyResult.getCode(), strategyResult.getNum(), strategyResult.getPrice()));
                }
                if (StrategyCallType.SELL.compareTo(strategyResult.getStrategyCallType()) == 0) {
                    contents.append(String.format("[操作][卖出]目标:%s, 数量:%s, 价格:%s \n", strategyResult.getCode(), strategyResult.getNum(), strategyResult.getPrice()));
                }
            }
        }

        NotifyModel notifyModel = new NotifyModel();
        notifyModel.setTitle(title);
        notifyModel.setOriContent(contents.toString());
        return notifyModel;
    }

    private void computeFee(List<StrategyResult> strategyResults) {
        for (StrategyResult strategyResult : strategyResults) {
            Long price = strategyResult.getPrice();
            Long num = strategyResult.getNum();
            if (StrategyCallType.BUY.compareTo(strategyResult.getStrategyCallType()) == 0) {
                strategyResult.setFee(this.feeBuy(price, num));
            }
            if (StrategyCallType.SELL.compareTo(strategyResult.getStrategyCallType()) == 0) {
                strategyResult.setFee(this.feeSell(price, num));
            }
        }
    }

    /**
     * 处理交易数量
     * A股需要处理为：100股=1手
     * */
    protected void dealWithTradeNum(List<StrategyResult> strategyResults){
        for (StrategyResult strategyResult : strategyResults) {
            long num = strategyResult.getNum();
            strategyResult.setNum(num/100 * 100);
        }
    }

    public Long feeSell(Long price, Long num) {
        TradeAccountModel accountInfo = tradeSystemContext.getAccount();

        Integer sellFixed = accountInfo.getSellFixedFee();
        Long guohu = price * num * accountInfo.getSellFeeRateGuo() / GlobalContain.FEE_RATE_ARROUND;
        Long qunshang = price * num * accountInfo.getSellFeeRateQuan() / GlobalContain.FEE_RATE_ARROUND;
        Long yinhua = price * num * accountInfo.getSellFeeRateYin() / GlobalContain.FEE_RATE_ARROUND;
        return sellFixed + guohu + qunshang + yinhua;
    }

    public Long feeBuy(Long price, Long num) {
        TradeAccountModel accountInfo = tradeSystemContext.getAccount();
        Integer sellFixed = accountInfo.getBuyFixedFee();
        Long guohu = price * num * accountInfo.getBuyFeeRateGuo() / GlobalContain.FEE_RATE_ARROUND;
        Long qunshang = price * num * accountInfo.getBuyFeeRateQuan() / GlobalContain.FEE_RATE_ARROUND;
        return sellFixed + guohu + qunshang;
    }

    private void updatePriceToHoldPos(TradeSystemContext tradeSystemContext) {
        Map<String, PositionAccModel> holdPositionMap = tradeSystemContext.getHoldPositionMap();
        for (PositionAccModel holdPosModel : holdPositionMap.values()) {
            String tsCode = holdPosModel.getTsCode();
            Bar bars = this.dataCache.getcurrentBar().get(tsCode);
            Long close = bars.getClose();
            holdPosModel.setNowPrice(close);
        }
    }

    private void updateLimitOrder(TradeSystemContext tradeSystemContext){
        Map<String, List<TradeLimitOrderModel>> limitOrderMapCache = tradeSystemContext.getLimitOrderMap();
        // limitOrderMapCache中新的限价单入库
        limitOrderMapCache.forEach((k,v)-> {
            v.forEach(t ->{
                if (t.getLimitId() == null){
                    // id为空，新的限价单，入库
                    t.setLimitId(idWorkerUtil.nowId(IdWorkerUtil.IdType.LIMIT_ID));
                    limitOrderService.insertLimitOrderSelective(t);
                } else {
                    // 老的限价单，更新
                    limitOrderService.updateLimitOrderSelective(t);
                }
            });
        });
    }

    private void busyCheck(boolean isBusy) {
        if (this.distributeManager.isBusy() || isBusy) {
//            this.notifyInfo(tradeSystemContext, this.buildNotifyModel(null, "系统告警", "系统处理数据过多", GlobalContain.NotifyType.SYSTEM));
            if (this.tradeModel.getMillis() < 5 * 60 * 1000L) {
                this.tradeModel.setMillis(this.tradeModel.getMillis() + 10 * 1000L);
            }
        } else {
            if (this.tradeModel.getMillis() > this.minSleepTime) {
                if (this.tradeModel.getMillis() - 10 * 1000L <= this.minSleepTime) {
                    this.tradeModel.setMillis(this.minSleepTime);
                } else {
                    this.tradeModel.setMillis(this.tradeModel.getMillis() - 10 * 1000L);
                }
            }
        }
    }

    private List<StrategyResult> getStrategyResults(Map<String, IndicatorResult> indicatorResultsMap) {
        List<StrategyResult> res = new ArrayList<>();
        log.info("计算策略结果..., workerId:{}", tradeSystemContext.getWorkerId());
        for (String targetCode : this.targetStrategyMap.keySet()) {
            TradeStrategy strategy = this.targetStrategyMap.get(targetCode);
            IndicatorResult indicatorResult = indicatorResultsMap.get(targetCode);
            StrategyResult exec = strategy.exec(targetCode, indicatorResult, this.tradeSystemContext);
            exec.setStrategyCode(strategy.getStrategyCode());
            exec.setIndicatorResult(indicatorResult);
            res.add(exec);
        }
        return res;
    }

    private void updateContext(boolean show){
        log.info("实时更新上下文...");
        //更新持仓信息
        this.updateHoldPos(tradeSystemContext);

        //更新最新价格到持仓
        this.updatePriceToHoldPos(tradeSystemContext);

        // 更新限价单
//        this.updateLimitOrder(tradeSystemContext);

        if (show){
            this.showHoldPos();
        }
    }

    private void showHoldPos(){
        Map<String, PositionAccModel> holdPositionMap = tradeSystemContext.getHoldPositionMap();
        log.info("<当前持仓信息>");
        holdPositionMap.forEach((k,v) -> {
            log.info("<{}:共计持有[{}]股, 可交易[{}]股, 当前价格:{}>", k, v.getNumTotal(), v.getNumCanTrade(), v.getNowPrice());
        });
    }


    private void endFrequency(){
        log.info("周期最后执行...");
        for (String sgCode : this.targetStrategyMap.keySet()) {
            TradeStrategy strategy = this.targetStrategyMap.get(sgCode);
            strategy.updateEnd(tradeSystemContext);
        }
    }

}
